from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import TextIO

MAX_MEM_SIZE = 100000
MAX_REQ_NUM = 10000
MAX_TIME = 10**9
MEM_BANDWIDTH = 40


@dataclass
class Settings:
    def __init__(self, L: int, M: int, N: int) -> None:
        assert 1 <= M <= L <= MAX_MEM_SIZE, "Constraints on L and M are not satisfied."
        assert 1 <= N <= MAX_REQ_NUM, "Constraints on N are not satisfied."
        self.L = L  # Virtual memory size
        self.M = M  # Physical memory size
        self.N = N  # Number of requests

    def __repr__(self):
        return f"Settings(L={self.L}, M={self.M}, N={self.N})"


class Action(ABC):
    def __init__(self, time: int) -> None:
        self.time = time

    @abstractmethod
    def cost(self) -> int:
        raise NotImplementedError

    @abstractmethod
    def __str__(self) -> str:
        raise NotImplementedError

    @abstractmethod
    def __repr__(self):
        return NotImplementedError

    @classmethod
    def from_textio(
        cls, settings: Settings, requests: list[Request], textio: TextIO
    ) -> list[Action]:
        lines = textio.readlines()
        actions: list[Action] = []
        for line in lines:
            parts = line.strip().split()
            action_type = parts[0]

            match action_type:
                case "Reload":
                    time, addr, size = map(int, parts[1:])
                    actions.append(ReloadAction(settings, time, addr, size))
                case "Visit":
                    time, index = map(int, parts[1:])
                    assert 0 <= index < settings.N, "Request index is out of bounds."
                    actions.append(VisitAction(settings, time, index, requests[index]))
                case "Offload":
                    time, addr, size = map(int, parts[1:])
                    actions.append(OffloadAction(settings, time, addr, size))
                case "Fin":
                    time = int(parts[1])
                    actions.append(FinAction(time))
                case _:
                    raise ValueError(f"Unknown action type: {action_type}")

        return actions

    @classmethod
    def into_textio(cls, textio: TextIO, actions: list[Action]) -> None:
        for action in actions:
            textio.write(str(action) + "\n")


class ReloadAction(Action):
    def __init__(self, settings: Settings, time: int, addr: int, size: int) -> None:
        super().__init__(time)
        assert 0 <= addr < settings.L, "Address is out of bounds."
        assert 1 <= addr + size <= settings.L, "Size exceeds memory limit."
        self.addr = addr
        self.size = size

    def cost(self) -> int:
        return MEM_BANDWIDTH * self.size

    def __str__(self) -> str:
        return f"Reload {self.time} {self.addr} {self.size}"

    def __repr__(self):
        return f"ReloadAction(time={self.time}, addr={self.addr}, size={self.size})"


class VisitAction(Action):
    def __init__(self, settings: Settings, time: int, index: int, req: Request) -> None:
        super().__init__(time)
        assert 0 <= index < settings.N, "Request index is out of bounds."
        self.index = index
        self.request = req

    def cost(self) -> int:
        return self.request.time

    def __str__(self) -> str:
        return f"Visit {self.time} {self.index}"

    def __repr__(self):
        return f"VisitAction(time={self.time}, index={self.index}, request={repr(self.request)})"


class OffloadAction(Action):
    def __init__(self, settings: Settings, time: int, addr: int, size: int) -> None:
        super().__init__(time)
        assert 0 <= addr < settings.L, "Address is out of bounds."
        assert 1 <= addr + size <= settings.L, "Size exceeds memory limit."
        self.addr = addr
        self.size = size

    def cost(self) -> int:
        return MEM_BANDWIDTH * self.size

    def __str__(self) -> str:
        return f"Offload {self.time} {self.addr} {self.size}"

    def __repr__(self):
        return f"OffloadAction(time={self.time}, addr={self.addr}, size={self.size})"


class FinAction(Action):
    def __init__(self, time: int) -> None:
        super().__init__(time)

    def cost(self) -> int:
        return 0

    def __str__(self) -> str:
        return f"Fin {self.time}"

    def __repr__(self):
        return f"FinAction(time={self.time})"


@dataclass
class Request:
    def __init__(
        self, settings: Settings, addr: int, size: int, start: int, time: int
    ) -> None:
        assert 0 <= addr < settings.L, "Address is out of bounds."
        assert 1 <= addr + size <= settings.L, "Size exceeds memory limit."
        assert 0 <= start, "Start time is out of bounds."
        assert 1 <= start + time <= MAX_TIME, "Duration time is out of bounds."
        self.addr = addr
        self.size = size
        self.start = start
        self.time = time

    def __eq__(self, value):
        if not isinstance(value, Request):
            return False
        return (
            self.addr == value.addr
            and self.size == value.size
            and self.start == value.start
            and self.time == value.time
        )

    def __str__(self):
        return f"{self.addr} {self.size} {self.start} {self.time}"

    def __repr__(self):
        return f"Request(addr={self.addr}, size={self.size}, start={self.start}, time={self.time})"

    @classmethod
    def from_textio(cls, textio: TextIO) -> list[Request]:
        lines = textio.readlines()
        L, M, N = map(int, lines[0].strip().split())
        settings = Settings(L, M, N)

        assert len(lines) == N + 1, "Number of lines does not match N."

        requests = []
        for i in range(1, N + 1):
            addr, size, start, time = map(int, lines[i].strip().split())
            requests.append(Request(settings, addr, size, start, time))

        return settings, requests

    @classmethod
    def into_textio(
        cls, textio: TextIO, settings: Settings, requests: list[Request]
    ) -> None:
        assert len(requests) == settings.N, "Number of requests does not match N."
        textio.write(f"{settings.L} {settings.M} {settings.N}\n")
        for req in requests:
            textio.write(str(req) + "\n")
